package com.sinodata.bsm.cicp.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

import com.sinodata.bsm.common.vo.ProtocolParameter;

/**
 * 
 * <p>
 * Description: Snmp工具类
 * </p>
 *
 * @author liulibo
 * @version 1.0

 * <p>
 * History: 
 *
 * Date                     Author         Version     Description
 * ---------------------------------------------------------------------------------
 * 2012-5-21 上午11:19:20   liulibo        1.0         To create
 * </p>
 *
 * @since 
 * @see
 */
public class SnmpUtil {

    private static final Logger logger = Logger.getLogger(com.sinodata.bsm.cicp.util.SnmpUtil.class.getName());

    private final static int DEFAULT_SNMP_PORT = 161;

    private final static String community = "public";

    private static int tryTimes = 1;

    private static long timeOut = 5000;

    private static int port = 161;
    private static int ver = 1;
    //  private static int tryTimes = 2;
    // private static long timeOut = 2000;

    private static Map<String, SnmpAgent> snmpPool = new HashMap<String, SnmpAgent>();

    public static void main(String[] args) {
        String val = getValueBySnmp("172.168.1.82", ".1.3.6.1.2.1.1.1.0");
        System.out.println(val);
    }

    /**
     * 根据snmp协议获取值，snmp的端口根据系统的配置中获取 系统中参数配置格式:1）type为3，2）name为IP，3）value字段为<port
     * 161/>
     *
     * @param ip
     * @param oid
     * @return
     */
    public synchronized static String getValueBySnmp(String ip, String oid) {
        String str = null;
        try {
            SnmpAgent snmpAgent = getSnmpConnection(ip);
            Snmp snmp = snmpAgent.getSnmp(); //获取 snmp
            CommunityTarget target = snmpAgent.getTarget(); //获取agent对象
            PDU request = new PDU();
            request.setType(PDU.GET);// PDU形式
            request.add(new VariableBinding(new OID(oid))); // OID添加

            PDU response = null;// 定义response包
            ResponseEvent responseEvent = snmp.send(request, target); // 发出request
            // 接收response PDU
            response = responseEvent.getResponse();

            if (response != null) {
                if (response.getErrorIndex() == PDU.noError && response.getErrorStatus() == PDU.noError) {
                    String value = response.getVariableBindings().toString();
                    String s[] = value.split("=");
                    str = s[1].substring(0, s[1].length() - 1).trim();
                } else {
                    str = null;
                }
            } else {
                str = null;
            }
        } catch (IOException e) {
            logger.error(e);
        }
        return str;
    }

    public synchronized static String[] getValuesBySnmp(String ip, String[] oids) {
        return null;
    }

    private synchronized static SnmpAgent getSnmpConnection(String ip) throws IOException {
        SnmpAgent agent = null;
        String version = "";
        if (snmpPool.containsKey(ip)) {
            agent = snmpPool.get(ip);
        } else {
            int port = DEFAULT_SNMP_PORT;
            ProtocolParameter protocolParameter = ProtocolParameterUtil.getSnmpValue(ip);
            if (protocolParameter != null) {
                port = protocolParameter.getPort();
                version = protocolParameter.getProtocol();
            }
            int ver = 0;
            if (version.equals("v1")) {
                ver = SnmpConstants.version1;
            } else if (version.equals("v2c")) {
                ver = SnmpConstants.version2c;
            } else {
                ver = SnmpConstants.version3;
            }
            agent = new SnmpAgent();
            Snmp snmp = new Snmp(new DefaultUdpTransportMapping());
            snmp.listen();
            String address = ip.trim() + "/" + port;
            CommunityTarget target = new CommunityTarget();// agent对象
            target.setCommunity(new OctetString(community));// 设置共同体名
            target.setVersion(ver);// 设置版本 ?版本
            target.setAddress(new UdpAddress(address));// 设置IP地址和端口号
            target.setRetries(tryTimes);// 设置重试次数
            target.setTimeout(timeOut);// 设置超时
            agent.setSnmp(snmp);
            agent.setTarget(target);
            snmpPool.put(ip, agent);
        }
        return agent;
    }

    /**
     *用snmp协议获取确定OID号的数据
     * @param ip
     * @param oids
     * @return resultList
     * @throws Exception
     * @throws Exception
     */
    public synchronized static Map<Integer, String> getValueBySnmp(String ip, String[] oids, String community) throws Exception {
        Map<Integer, String> resultMap = new HashMap<Integer, String>();
        Snmp snmp = null;
        DefaultUdpTransportMapping udpTransportMap = null;
        getMonitorParam(ip);
        Address targetAddress = GenericAddress.parse(ip.trim() + "/" + port);
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(community));
        target.setVersion(ver);
        target.setAddress(targetAddress);
        target.setRetries(tryTimes);
        target.setTimeout(timeOut);

        try {
            udpTransportMap = new DefaultUdpTransportMapping();
            snmp = new Snmp(udpTransportMap);
            snmp.listen();
            PDU request = new PDU();
            request.setType(PDU.GET);
            for (String oid : oids) {
                request.add(new VariableBinding(new OID(oid)));
            }
            PDU response = null;
            ResponseEvent responseEvent = snmp.send(request, target);
            response = responseEvent.getResponse();

            if (response != null) {
                if (response.getErrorIndex() == PDU.noError && response.getErrorStatus() == PDU.noError) {
                    Vector<VariableBinding> recVBs = response.getVariableBindings();
                    for (int i = 0; i < recVBs.size(); i++) {
                        VariableBinding recVB = recVBs.elementAt(i);
                        if (recVB.getSyntax() == 4) {
                            resultMap.put(i, displaySnmpInfo(recVB.getVariable().toString()));
                        } else {
                            resultMap.put(i, recVB.getVariable().toString());
                        }
                    }
                }
            }
            snmp.close();
            logger.info("snmp connect successed!");
        } catch (Exception e) {
            logger.error("snmp connect failed!", e);
            throw new Exception(e.getMessage());
        } finally {
            if (snmp != null) {
                try {
                    snmp.close();
                } catch (IOException ex1) {
                    snmp = null;
                }
            }
            if (udpTransportMap != null) {
                try {
                    udpTransportMap.close();
                } catch (IOException ex2) {
                    udpTransportMap = null;
                }
            }
        }
        return resultMap;
    }

    /**
     * 用snmp协议获取table的数据
     * @param ip
     * @param OIDS
     * @param community
     * @return
     * @throws Exception
     */
    public synchronized static String getValueBySnmpTable(String ip, String OIDS, String community) throws Exception {
        StringBuffer getValue = new StringBuffer();
        TransportMapping transport = null;
        Snmp snmp = null;
        getMonitorParam(ip);
        Address targetAddress = GenericAddress.parse(ip.trim() + "/" + port);
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(community));
        target.setAddress(targetAddress);
        target.setVersion(ver);
        target.setTimeout(timeOut);
        target.setRetries(tryTimes);
        PDU requestPDU = new PDU();
        requestPDU.setType(PDU.GETNEXT);
        String[] oids = OIDS.split("[,]");
        OID[] targetOID = new OID[oids.length];
        for (int i = 0; i < oids.length; i++) {
            targetOID[i] = new VariableBinding(new OID(oids[i])).getOid();
            requestPDU.add(new VariableBinding(new OID(oids[i])));
        }

        try {
            transport = new DefaultUdpTransportMapping();
            snmp = new Snmp(transport);
            transport.listen();
            boolean finished = false;
            while (!finished) {
                VariableBinding vb = null;
                ResponseEvent response = snmp.send(requestPDU, target);
                PDU responsePDU = response.getResponse();

                if (null == responsePDU) {
                    System.out.println("responsePDU == null");
                    finished = true;
                    break;
                } else {
                    vb = responsePDU.get(0);
                }
                finished = checkWalkFinished(targetOID[0], responsePDU, vb);
                if (!finished) {
                    Vector<VariableBinding> recVBs = responsePDU.getVariableBindings();
                    for (int j = 0; j < recVBs.size(); j++) {
                        VariableBinding recVB = recVBs.elementAt(j);
                        if (recVB.getSyntax() == 4) {
                            getValue.append(displaySnmpInfo(recVB.getVariable().toString()) + "#");
                        } else {
                            getValue.append(recVB.getVariable() + "#");
                        }
                        requestPDU.set(j, recVB);
                    }
                    getValue.append("\n");
                }
            }
            logger.info("snmp connect successed!");
        } catch (Exception e) {
            logger.error("snmp connect failed!", e);
            throw new Exception(e);
        } finally {
            if (snmp != null) {
                try {
                    snmp.close();
                } catch (IOException ex1) {
                    snmp = null;
                }
            }
            if (transport != null) {
                try {
                    transport.close();
                } catch (IOException ex2) {
                    transport = null;
                }
            }
        }
        return getValue.toString();
    }

    /**
     * 用snmp协议获取table的数据,注意此方法和 getValueBySnmpTable 不同在于，仅仅不翻译十六进制的编码值
     * @param ip
     * @param OIDS
     * @param community
     * @return
     * @throws Exception
     */
    public synchronized static String getValueBySnmpTable2(String ip, String OIDS, String community) throws Exception {
        StringBuffer getValue = new StringBuffer();
        TransportMapping transport = null;
        Snmp snmp = null;
        getMonitorParam(ip);
        Address targetAddress = GenericAddress.parse(ip.trim() + "/" + port);
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(community));
        target.setAddress(targetAddress);
        target.setVersion(ver);
        target.setTimeout(timeOut);
        target.setRetries(tryTimes);
        PDU requestPDU = new PDU();
        requestPDU.setType(PDU.GETNEXT);
        String[] oids = OIDS.split("[,]");
        OID[] targetOID = new OID[oids.length];
        for (int i = 0; i < oids.length; i++) {
            targetOID[i] = new VariableBinding(new OID(oids[i])).getOid();
            requestPDU.add(new VariableBinding(new OID(oids[i])));
        }

        try {
            transport = new DefaultUdpTransportMapping();
            snmp = new Snmp(transport);
            transport.listen();
            boolean finished = false;
            while (!finished) {
                VariableBinding vb = null;
                ResponseEvent response = snmp.send(requestPDU, target);
                PDU responsePDU = response.getResponse();

                if (null == responsePDU) {
                    System.out.println("responsePDU == null");
                    finished = true;
                    break;
                } else {
                    vb = responsePDU.get(0);
                }
                finished = checkWalkFinished(targetOID[0], responsePDU, vb);
                if (!finished) {
                    Vector<VariableBinding> recVBs = responsePDU.getVariableBindings();
                    for (int j = 0; j < recVBs.size(); j++) {
                        VariableBinding recVB = recVBs.elementAt(j);
                        getValue.append(recVB.getVariable() + "#");
                        requestPDU.set(j, recVB);
                    }
                    getValue.append("\n");
                }
            }
            logger.info("snmp connect successed!");
        } catch (Exception e) {
            logger.error("snmp connect failed!", e);
            throw new Exception(e);
        } finally {
            if (snmp != null) {
                try {
                    snmp.close();
                } catch (IOException ex1) {
                    snmp = null;
                }
            }
            if (transport != null) {
                try {
                    transport.close();
                } catch (IOException ex2) {
                    transport = null;
                }
            }
        }
        return getValue.toString();
    }

    /**
     * 根据vb参数判断next OID是不是超出遍历的范围，没有超出返回false，超出范围返回true
     * @param targetOID
     * @param responsePDU
     * @param vb
     * @return
     */
    private static boolean checkWalkFinished(OID targetOID, PDU responsePDU, VariableBinding vb) {
        boolean finished = false;
        if (responsePDU.getErrorStatus() != 0) {
            finished = true;
        } else if (vb.getOid() == null) {
            finished = true;
        } else if (vb.getOid().size() < targetOID.size()) {
            finished = true;
        } else if (targetOID.leftMostCompare(targetOID.size(), vb.getOid()) != 0) {
            finished = true;
        } else if (Null.isExceptionSyntax(vb.getVariable().getSyntax())) {
            finished = true;
        } else if (vb.getOid().compareTo(targetOID) <= 0) {
            finished = true;
        }
        return finished;
    }

    /**
     * 转换OctetString类型中中文乱码
     * @param octetString
     * @return
     */
    public static String displaySnmpInfo(String octetString) {
        String snmpInfo = octetString.trim();
        Pattern hexPtn = Pattern.compile("[0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*");
        Matcher hexMtr = hexPtn.matcher(snmpInfo);

        if (snmpInfo == null || "".equals(snmpInfo)) {
            return "";
        }

        if (!hexMtr.matches()) {
            return snmpInfo;
        }

        String[] snmpChars = snmpInfo.split(":");
        int length = snmpChars.length;
        byte[] snmpBytes = new byte[length];
        for (int i = 0; i < length; i++) {
            snmpBytes[i] = (byte) Integer.parseInt(snmpChars[i].trim(), 16);
        }
        if (snmpBytes[length - 1] == 0x00) {
            snmpBytes[length - 1] = 0x20;
        }

        try {
            return new String(snmpBytes, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据snmp协议获取值，snmp的端口根据系统的配置中获取 系统中参数配置格式:1）type为3，2）name为IP，3）value字段为<port
     * 161/>
     * @param ip
     * @throws Exception
     */
    private static void getMonitorParam(String ip) throws Exception {
        String version = "";
        ProtocolParameter protocolParameter = ProtocolParameterUtil.getSnmpValue(ip);
        if (protocolParameter != null) {
            port = protocolParameter.getPort();
            version = protocolParameter.getProtocol();
        }
        if (version.equals("v1")) {
            ver = SnmpConstants.version1;
        } else if (version.equals("v2c")) {
            ver = SnmpConstants.version2c;
        } else {
            ver = SnmpConstants.version3;
        }
    }

}

class SnmpAgent {

    private Snmp snmp = null;

    private CommunityTarget target = null;

    public Snmp getSnmp() {
        return snmp;
    }

    public void setSnmp(Snmp snmp) {
        this.snmp = snmp;
    }

    public CommunityTarget getTarget() {
        return target;
    }

    public void setTarget(CommunityTarget target) {
        this.target = target;
    }

}